class BilliardMulti extends egret.DisplayObjectContainer {
    private balls: Array<Ball>
    private numBalls: number = 8
    private bounce: number = -1.0
    public constructor() {
        super()
        this.once(egret.Event.ADDED_TO_STAGE, this.onAddToStage, this)
    }

    private onAddToStage(evt: egret.Event): void {
        this.balls = new Array<Ball>()
        for (let i = 0; i < this.numBalls; ++i) {
            let radius = Math.random() * 20 + 40
            let ball = new Ball(radius)
            ball.mass = radius
            ball.x = i * 100
            ball.y = i * 50
            ball.vx = Math.random() * 10 - 5
            ball.vy = Math.random() * 10 - 5
            this.balls.push(ball)
            this.addChild(ball)
        }
        this.addEventListener(egret.Event.ENTER_FRAME, this.onEnterFrame, this)
    }

    private onEnterFrame(evt: egret.Event): void {
        for (let ball of this.balls) {
            ball.x += ball.vx
            ball.y += ball.vy
            this.checkWalls(ball)
        }
       
        for (let i = 0; i < this.numBalls - 1; ++i) {
            let ballA = this.balls[i]
            for (let j = i + 1; j < this.numBalls; ++j) {
                let ballB = this.balls[j]
                this.checkCollection(ballA, ballB)
            }
        }
    }

    private checkCollection(ball0: Ball, ball1: Ball) {
        let dx = ball1.x - ball0.x
        let dy = ball1.y - ball0.y
        let dist = Math.sqrt(dx * dx + dy * dy)

        if (dist < ball0.radius + ball1.radius) {

            let angle = Math.atan2(dy, dx)
            let sin = Math.sin(angle)
            let cos = Math.cos(angle)

            // 旋转ball0的位置，是按照ball0进行旋转的，那么自己相对于自己的相对位置是0
            let pos0 = this.rotate(0, 0, sin, cos, true)

            // 旋转ball1的位置，已知相对位置分别为dx, dy，那么直接进行旋转操作
            let pos1 = this.rotate(dx, dy, sin, cos, true)

            // 旋转ball0的速度
            let vel0 = this.rotate(ball0.vx, ball0.vy, sin, cos, true)

            // 旋转ball1的速度
            let vel1 = this.rotate(ball1.vx, ball1.vy, sin, cos, true)

            // 碰撞反应
            let vxTotal = vel0.x - vel1.x
            vel0.x = ((ball0.mass - ball1.mass) * vel0.x + 2 * ball1.mass * vel1.x) / (ball0.mass + ball1.mass)
            vel1.x = vxTotal + vel0.x

            // 更新位置
            let absV = Math.abs(vel0.x) + Math.abs(vel1.x)
            let overlap = (ball0.radius + ball1.radius) - Math.abs(pos0.x - pos1.x)
            pos0.x += vel0.x / absV * overlap
            pos1.x += vel1.x / absV * overlap

            // 把位置旋转回去
            let pos0F = this.rotate(pos0.x, pos0.y, sin, cos, false)
            let pos1F = this.rotate(pos1.x, pos1.y, sin, cos, false)
            
            // 将位置调整到实际的屏幕位置
            ball1.x = ball0.x + pos1F.x
            ball1.y = ball0.y + pos1F.y
            ball0.x = ball0.x + pos0F.x
            ball0.y = ball0.y + pos0F.y

            // 将速度旋转回去
            let vel0F = this.rotate(vel0.x, vel0.y, sin, cos, false)
            let vel1F = this.rotate(vel1.x, vel1.y, sin, cos, false)
            ball0.vx = vel0F.x
            ball0.vy = vel0F.y
            ball1.vx = vel1F.x
            ball1.vy = vel1F.y
        }
    }

    private checkWalls(ball: Ball) {
        if (ball.x + ball.radius > this.stage.stageWidth) {
            ball.x = this.stage.stageWidth - ball.radius
            ball.vx *= this.bounce
        }
        else if (ball.x - ball.radius < 0) {
            ball.x = ball.radius
            ball.vx *= this.bounce
        }
        if (ball.y + ball.radius > this.stage.stageHeight) {
            ball.y = this.stage.stageHeight - ball.radius
            ball.vy *= this.bounce
        }
        else if (ball.y - ball.radius < 0) {
            ball.y = ball.radius
            ball.vy *= this.bounce
        }
    }

    private rotate(x: number, y: number, sin: number, cos: number, reverse: boolean): { x: number; y: number; } {
        let result = { x: 0, y: 0 }
        if (reverse) {
            result.x = x * cos + y * sin
            result.y = y * cos - x * sin
        } else {
            result.x = x * cos - y * sin
            result.y = y * cos + x * sin
        }
        return result
    }
}